package cn.juque.datapipeline.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.juque.common.constants.NumberConstant;
import cn.juque.datapipeline.api.enums.GroupExecuteStatusEnum;
import cn.juque.datapipeline.bo.statistics.MostUseTimeGroupBO;
import cn.juque.datapipeline.bo.statistics.TaskRunDistributeBO;
import cn.juque.datapipeline.bo.statistics.TotalStatisticsBO;
import cn.juque.datapipeline.domain.TaskExecuteInfoDO;
import cn.juque.datapipeline.domain.TaskExecuteInfoSearchParamDO;
import cn.juque.datapipeline.entity.TaskExecuteInfo;
import cn.juque.datapipeline.entity.TaskGroupInfo;
import cn.juque.datapipeline.mapper.TaskExecuteInfoMapper;
import cn.juque.datapipeline.mapper.TaskGroupInfoMapper;
import cn.juque.datapipeline.service.IStatisticsService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>StatisticsServiceImpl</li>
 * </ul>
 * @date 2023-04-11 23:22:13
 **/
@Service("statisticsService")
public class StatisticsServiceImpl implements IStatisticsService {

    @Resource
    private TaskGroupInfoMapper taskGroupInfoMapper;

    @Resource
    private TaskExecuteInfoMapper taskExecuteInfoMapper;

    /**
     * 统计任务组数量
     *
     * @return TotalStatisticsBO
     */
    @Override
    public TotalStatisticsBO totalGroupCount() {
        List<TaskGroupInfo> list = taskGroupInfoMapper.selectList(new QueryWrapper<>(new TaskGroupInfo()));
        Integer groupCount = CollUtil.size(list);
        long groupSuccessCount = list.stream()
                .filter(f -> GroupExecuteStatusEnum.END.getCode().equals(f.getExecuteStatus())).count();
        long groupFailCount = list.stream()
                .filter(f -> GroupExecuteStatusEnum.FAIL.getCode().equals(f.getExecuteStatus())).count();
        long groupIngCount = list.stream()
                .filter(f -> GroupExecuteStatusEnum.ING.getCode().equals(f.getExecuteStatus())).count();
        TotalStatisticsBO bo = new TotalStatisticsBO();
        bo.setGroupCount(groupCount);
        bo.setGroupSuccessCount((int) groupSuccessCount);
        bo.setGroupFailCount((int) groupFailCount);
        bo.setGroupIngCount((int) groupIngCount);
        return bo;
    }

    /**
     * 统计任务执行的分布状态
     *
     * @param date 日期
     * @return TaskRunDistributeBO
     */
    @Override
    public TaskRunDistributeBO statisticsDistribute(String date) {
        String createDate = date;
        if (CharSequenceUtil.isEmpty(createDate)) {
            createDate = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        }
        TaskExecuteInfoSearchParamDO searchParamDO = new TaskExecuteInfoSearchParamDO();
        searchParamDO.setCreateDate(createDate);
        List<TaskExecuteInfoDO> list = this.taskExecuteInfoMapper.queryExecuteInfoList(searchParamDO);
        // 根据任务组分组，同时内部根据创建时间升序排序
        Map<String, List<TaskExecuteInfoDO>> statisticsMap = list.stream()
                .collect(Collectors.groupingBy(TaskExecuteInfo::getExecuteBatchNo, Collectors.toList()));
        Map<Integer, Integer> startMap = new LinkedHashMap<>(12);
        Map<Integer, Integer> endMap = new LinkedHashMap<>(12);
        int offset = 2;
        for(int i=1;i<=12;i++) {
            startMap.put(offset * i, 0);
            endMap.put(offset * i, 0);
        }
        statisticsMap.forEach((k,v)->{
            if (CollUtil.isEmpty(v)) {
                return;
            }
            List<TaskExecuteInfoDO> subList = v.stream()
                    .sorted(Comparator.comparing(TaskExecuteInfo::getStartTime)).collect(Collectors.toList());
            Date startDate = subList.get(0).getStartTime();
            Date endDate = subList.get(subList.size()-1).getEndTime();
            if (null == endDate) {
                endDate = new Date();
            }
            int startTimeRange = this.timeRange(startDate);
            int count = startMap.get(startTimeRange);
            count++;
            startMap.put(startTimeRange, count);
            int endTimeRange = this.timeRange(endDate);
            count = endMap.get(endTimeRange);
            count++;
            endMap.put(endTimeRange, count);

        });
        TaskRunDistributeBO bo = new TaskRunDistributeBO();
        bo.setStartTaskList(CollUtil.newArrayList(startMap.values()));
        bo.setEndTaskList(CollUtil.newArrayList(endMap.values()));
        return bo;
    }

    /**
     * 统计最耗时任务组
     *
     * @param date 日期
     * @return TaskRunDistributeBO
     */
    @Override
    public MostUseTimeGroupBO statisticsMostUseTime(String date) {
        String createDate = date;
        if (CharSequenceUtil.isEmpty(createDate)) {
            createDate = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        }
        TaskExecuteInfoSearchParamDO searchParamDO = new TaskExecuteInfoSearchParamDO();
        searchParamDO.setCreateDate(createDate);
        List<TaskExecuteInfoDO> list = this.taskExecuteInfoMapper.queryExecuteInfoList(searchParamDO);
        if(CollUtil.isEmpty(list)) {
            MostUseTimeGroupBO bo = new MostUseTimeGroupBO();
            bo.setGroupNameList(CollUtil.newArrayList());
            bo.setUseTimeList(CollUtil.newArrayList());
            return bo;
        }
        // 根据任务组分组，同时内部根据创建时间升序排序
        Map<String, List<TaskExecuteInfoDO>> statisticsMap = list.stream()
                .collect(Collectors.groupingBy(TaskExecuteInfo::getExecuteBatchNo, Collectors.toList()));
        Map<String, Long> groupIdRelMinuteMap = MapUtil.newHashMap();
        statisticsMap.forEach((k,v)->{
            if (CollUtil.isEmpty(v)) {
                return;
            }
            List<TaskExecuteInfoDO> subList = v.stream()
                    .sorted(Comparator.comparing(TaskExecuteInfo::getStartTime)).collect(Collectors.toList());
            Date endDate = subList.get(subList.size()-1).getEndTime();
            if(null == endDate) {
                return;
            }
            Date startDate = subList.get(0).getStartTime();
            Long existsRange = groupIdRelMinuteMap.get(subList.get(0).getTaskGroupId());
            long range = DateUtil.between(startDate, endDate, DateUnit.MINUTE);
            if(existsRange != null && existsRange > range) {
                range = existsRange;
            }
            groupIdRelMinuteMap.put(subList.get(0).getTaskGroupId(), range);
        });
        Set<String> groupIdSet = list.stream().map(TaskExecuteInfo::getTaskGroupId).collect(Collectors.toSet());
        List<TaskGroupInfo> groupList = this.taskGroupInfoMapper.selectBatchIds(groupIdSet);
        groupList = groupList.stream()
                .sorted(Comparator.comparing(c->groupIdRelMinuteMap.get(c.getId()), Comparator.reverseOrder()))
                .collect(Collectors.toList());
        groupList = CollUtil.sub(groupList, 0, 10);
        List<String> groupNames = groupList.stream().map(TaskGroupInfo::getGroupName).collect(Collectors.toList());
        List<Long> useTimeList = groupList.stream().map(m->groupIdRelMinuteMap.get(m.getId())).collect(Collectors.toList());
        MostUseTimeGroupBO bo = new MostUseTimeGroupBO();
        bo.setGroupNameList(groupNames);
        bo.setUseTimeList(useTimeList);

        return bo;
    }

    private Integer timeRange(Date date) {
        int startHour = DateUtil.hour(date, true);
        if(startHour >=NumberConstant.ZERO && startHour <NumberConstant.TWO) {
            return NumberConstant.TWO;
        }
        if(startHour >=NumberConstant.TWO && startHour <NumberConstant.FOUR) {
            return NumberConstant.FOUR;
        }
        if(startHour >=NumberConstant.FOUR && startHour <NumberConstant.SIX) {
            return NumberConstant.SIX;
        }
        if(startHour >=NumberConstant.SIX && startHour <NumberConstant.EIGHT) {
            return NumberConstant.EIGHT;
        }
        if(startHour >=NumberConstant.EIGHT && startHour <NumberConstant.TEN) {
            return NumberConstant.TEN;
        }
        if(startHour >=NumberConstant.TEN && startHour <NumberConstant.TWELVE) {
            return NumberConstant.TWELVE;
        }
        if(startHour >=NumberConstant.TWELVE && startHour <NumberConstant.FOURTEEN) {
            return NumberConstant.FOURTEEN;
        }
        if(startHour >=NumberConstant.FOURTEEN && startHour <NumberConstant.SIXTEEN) {
            return NumberConstant.SIXTEEN;
        }
        if(startHour >=NumberConstant.SIXTEEN && startHour <NumberConstant.EIGHTEEN) {
            return NumberConstant.EIGHTEEN;
        }
        if(startHour >=NumberConstant.EIGHTEEN && startHour <NumberConstant.TWENTY) {
            return NumberConstant.TWENTY;
        }
        if(startHour >=NumberConstant.TWENTY && startHour <NumberConstant.TWENTY_TWO) {
            return NumberConstant.TWENTY_TWO;
        }
        if(startHour >=NumberConstant.TWENTY_TWO && startHour <NumberConstant.TWENTY_FOUR) {
            return NumberConstant.TWENTY_FOUR;
        }
        return NumberConstant.TWO;
    }
}
